﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace Zocono.WCS.Infrastructure.SocketCommon
{
    public class SocketClient
    {
        public delegate void SocketReceiveDelegate(object deviceType, Socket socket, byte[] dataB, string dataS);
        public event SocketReceiveDelegate OnSocketReceive = null;
        private Socket client;
        private byte[] buf = new byte[1024 * 1024];
        public static object SendLock = new object();

        public virtual event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 属性变化事件
        /// </summary>
        /// <param name="name"></param>
        protected void NotifyPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }

        /// <summary>
        /// 服务器IP
        /// </summary>
        public string ServerIP { get; set; }


        /// <summary>
        /// 端口号
        /// </summary>
        public int ServerPort { get; set; }


        /// <summary>
        /// 心跳
        /// </summary>
        public bool SocketHeart { get; set; }

        /// <summary>
        /// 超时毫秒
        /// </summary>
        public int TimeOut
        {
            get; set;
        }

        /// <summary>
        /// 心跳秒
        /// </summary>
        public int TimeHeart { get; set; }


        private bool mIsConnected = false;
        /// <summary>
        /// 是否已连接
        /// </summary>
        public bool IsConnected
        {
            get { return mIsConnected; }
            set
            {
                if (mIsConnected != value)
                {

                    mIsConnected = value;
                    NotifyPropertyChanged("IsConnected");
                }
            }
        }

        /// <summary>
        /// 设备类型
        /// </summary>
        public object? DeviceType { get; set; }

        /// <summary>
        /// 设备类型转字符串
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.DeviceType.ToString();
        }


        /// <summary>
        /// 附属物
        /// </summary>
        public object Tag
        {
            get;
            set;
        }


        public SocketClient(string ip, int port, object deviceType)
        {
            this.ServerIP = ip;
            this.ServerPort = port;
            this.DeviceType = deviceType;
        }



        public string GetLocalIp()
        {
            IPEndPoint ep = (IPEndPoint)this.client.LocalEndPoint;
            return ((IPAddress)ep.Address).ToString();
        }

        public object obj_Start = new object();


        public void DoStart()
        {
            lock (obj_Start)
            {
                try
                {
                    if (this.client != null)
                    {
                        try
                        {
                            if (this.client.Connected)
                            {
                                this.client.Close();
                            }
                            this.client.Dispose();
                            this.client = null;
                        }
                        catch { }
                    }
                    try
                    {
                        this.client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        IPAddress ipad = IPAddress.Parse(this.ServerIP);
                        IPEndPoint iPEnd = new(ipad, this.ServerPort);
                        this.client.Connect(iPEnd);
                        this.client.BeginReceive(buf, 0, buf.Length, SocketFlags.None, new AsyncCallback(Recive), this.client);

                        this.client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.TimeOut);
                    }
                    catch
                    {

                    }
                    this.Timer_Connect_Elapsed(null, null);
                }
                catch
                {

                }
            }
        }


        public object buff_Lock = new object();

        /// <summary>
        /// 异步回调接收
        /// </summary>
        /// <param name="ia"></param>
        private void Recive(IAsyncResult ia)
        {
            try
            {
                lock (buff_Lock)
                {
                    client = ia.AsyncState as Socket;
                    int count = client.EndReceive(ia);
                    string context = Encoding.UTF8.GetString(buf, 0, count);
                    if (context.Length > 0)
                    {
                        //抛出事件
                        if (this.OnSocketReceive != null)
                        {
                            byte[] buf_tmp = new byte[count];
                            Array.Copy(buf, buf_tmp, count);//将buf复制到buf_tmp,复制长度count
                            this.OnSocketReceive.BeginInvoke(this.DeviceType, client, buf_tmp, context, null, this.OnSocketReceive);
                        }
                    }
                    client.BeginReceive(buf, 0, buf.Length, SocketFlags.None, new AsyncCallback(Recive), client);
                }
            }
            catch(Exception ex)
            {

            }
        }

        /// <summary>
        /// string发送数据
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool SendData(string data)
        {
            lock (SendLock)
            {
                bool ret = true;
                try
                {
                    byte[] buff;
                    buff = Encoding.UTF8.GetBytes(data);
                    int intR = client.Send(buff, 0, buff.Length, SocketFlags.None);
                }
                catch
                {
                    ret = false;
                }
                return ret;
            }
        }



        /// <summary>
        /// 发送字节类型
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool SendData(byte[] data)
        {
            lock (SendLock)
            {
                bool ret = true;
                try
                {
                    int intR = client.Send(data, 0, data.Length, SocketFlags.None);
                }
                catch
                {
                    ret = false;
                }
                return ret;
            }
        }


        /// <summary>
        /// 初始化
        /// </summary>
        public void DoInit()
        {
            try
            {
                this.InitTimer_Connect();
            }
            catch
            {
            }
        }

        public void DoStop()
        {
            try
            {
                this.StartTimer_Connect();
                if (client != null && client.Connected)
                {
                    this.client.Close();
                }
            }
            catch (Exception ex)
            {

                throw ex;
            }
        }


        public void DoRelease()
        {
            try
            {
                this.client.Dispose();
                this.client = null;
            }
            catch
            {
            }
        }



        #region 定时器重连
        private Timer Timer_Connect;


        public void InitTimer_Connect()
        {
            if (this.Timer_Connect == null) this.Timer_Connect = new Timer();

            this.Timer_Connect.Enabled = true;
            this.Timer_Connect.Interval = 1000 * (this.TimeHeart == 0 ? 2 : this.TimeHeart);//默认两秒
            this.Timer_Connect.Elapsed += new ElapsedEventHandler(Timer_Connect_Elapsed);
        }




        private void Timer_Connect_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                this.StopTimer_Connect();
                try
                {
                    if (this.client != null)
                    {
                        if (this.client.Poll(10, SelectMode.SelectRead) && this.client.Available == 0 || !this.client.Connected)
                            this.IsConnected = false;
                        else
                        {
                            if (Ping(this.ServerIP))
                                this.IsConnected = true;
                            else
                                this.IsConnected = false;
                        }
                    }
                    else
                        this.IsConnected = false;
                }
                catch
                {
                    this.IsConnected = false;
                }
                this.StartTimer_Connect();
            }
            catch
            {
            }
        }


        /// <summary>
        /// 开启定时器
        /// </summary>
        public void StartTimer_Connect()
        {
            this.Timer_Connect.Enabled = false;
            this.Timer_Connect.Enabled = true;
        }

        /// <summary>
        /// 停止定时器
        /// </summary>
        public void StopTimer_Connect()
        {
            this.Timer_Connect.Enabled = false;
        }
        #endregion



        /// <summary>
        /// 是否能PING通主机
        /// </summary>
        /// <param name="ip"></param>
        /// <returns></returns>
        public static bool Ping(string ip)
        {
            System.Net.NetworkInformation.Ping p = new System.Net.NetworkInformation.Ping();
            System.Net.NetworkInformation.PingOptions options = new System.Net.NetworkInformation.PingOptions();
            options.DontFragment = true;
            string data = "Test Data!";
            byte[] buffer = Encoding.UTF8.GetBytes(data);
            int timeout = 1000;
            System.Net.NetworkInformation.PingReply reply = p.Send(ip, timeout, buffer);
            if (reply.Status == System.Net.NetworkInformation.IPStatus.Success)
                return true;
            else
                return false;
        }
    }
}
